package com.atguigu.tingshu.common.Login;


import com.atguigu.tingshu.common.constant.RedisConstant;
import com.atguigu.tingshu.common.execption.GuiguException;
import com.atguigu.tingshu.common.result.ResultCodeEnum;
import com.atguigu.tingshu.common.util.AuthContextHolder;
import com.atguigu.tingshu.model.user.UserInfo;
import jakarta.servlet.http.HttpServletRequest;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;


@Component
@Aspect
public class TsLoginAspect {
    @Autowired
    private RedisTemplate redisTemplate;
    @Around("execution(* com.atguigu.tingshu.*.api.*.*(..)) && @annotation(tsLogin)")
    public Object doTsLoginAspect(ProceedingJoinPoint point, TsLogin tsLogin) throws Throwable {
        //  需要判断当前这个用户是否登录！ 一处登录，处处使用 --- 单点登录SSO; 1. 用户名，密码；2. 微信小程序登录 3. 第三方授权 4. 手机验证码
        //  登录过程： 利用微信小程序登录接口--通过前端传递的code码；调用api能够获取 openId【每个人的微信都有一个唯一的】;
        //  查询用户表；有登录成功-还会将这个用户信息保存到redis，没有注册！
        //  登录成功： 会生成一个token；token就是一个uuid; 将这个token返回给前端；并将这个token 保存到请求头;
        //  重点判断请求头中是否有token就可以了！前端还会将token存储到浏览器本地缓存中！ *****
        //  如何判断用户是否登录的?
        /*
            需要通过token 来判断用户是否登录!
            如果请求头中有token，则说明登录！否则，没有登录！
         */
        //  获取到请求头中的 token
        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
        ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) requestAttributes;
        HttpServletRequest request = servletRequestAttributes.getRequest();
        //  获取到token数据.
        String token = request.getHeader("token");
        //  编写用户在访问需要登录的业务时，没有登录，则跳转到登录业务逻辑.
        if (tsLogin.required()) {
            //  必须要登录！ 如何没有登录。则跳转!
            if (StringUtils.isEmpty(token)) {
                //  说明没有登录; 则抛出一个异常，页面根据这个异常就会跳转到登录页面！
                throw new GuiguException(ResultCodeEnum.LOGIN_AUTH);
            }
            //  token 不为空，则说明登录过！  还会将这个用户信息保存到redis
            //  此时需要进一步判断缓存中是否有用户信息,如果缓存中没有也不能说明登录！
            //  考虑登录时，缓存的key=token value=userInfo;
            String userLoginKey = RedisConstant.USER_LOGIN_KEY_PREFIX + token;
            //  获取缓存中用户数据
            UserInfo userInfo = (UserInfo) this.redisTemplate.opsForValue().get(userLoginKey);

            if (null == userInfo) {
                throw new GuiguException(ResultCodeEnum.LOGIN_AUTH);
            }
        }
        try {
            //  只要满足token 不为空的时候。就做一件事！将用户Id 存储到本地线程中！ 便于后台微服务获取用户Id！
            if (!StringUtils.isEmpty(token)) {
                //  组成缓存key
                String userLoginKey = RedisConstant.USER_LOGIN_KEY_PREFIX + token;
                //  获取缓存中用户数据
                UserInfo userInfo = (UserInfo) this.redisTemplate.opsForValue().get(userLoginKey);
                if (null != userInfo) {
                    //  存储 用户Id 到本地线程
                    AuthContextHolder.setUserId(userInfo.getId());
                }
            }
            //  执行方法体
            return point.proceed();
        } catch (Throwable e) {
            throw new RuntimeException(e);
        } finally {
            //  释放本地内存，防止ThreadLocal 内存泄漏！
            AuthContextHolder.removeUserId();
        }
    }
}
